多级页表查询
准备工作:场景设定
- 系统环境: 32位操作系统。
- 页表结构: 二级页表。
- 虚拟地址 (VA): 32位,结构如下:
| 10位 页目录索引 (DIR) | 10位 页表索引 (TABLE) | 12位 页内偏移 (OFFSET) |
- CPU中的关键部件:
- MMU (Memory Management Unit): 内存管理单元,硬件电路,负责执行地址转换。
- CR3 寄存器: CPU的一个特殊寄存器,存放着当前进程的页目录的物理基地址。每个进程切换时,操作系统会把新进程的页目录基地址加载到CR3中。
- TLB (Translation Lookaside Buffer): 一个高速缓存,用于存储最近用过的虚拟地址到物理地址的映射,以加速查询。
查询流程:一次完整的地址转换之旅
假设现在CPU要执行一条指令,比如 MOV EAX, [0x12345678]
,即从虚拟地址 0x12345678
读取一个数据。
第 0 步:检查高速缓存 TLB (捷径)
这是MMU做的第一件事,也是最常发生的情况。
-
MMU从虚拟地址
0x12345678
中提取出虚拟页号 (Virtual Page Number, VPN)。VPN就是去掉了页内偏移的部分,即0x12345
。 -
MMU拿着这个VPN (
0x12345
) 去查询TLB。 -
情况A:TLB 命中 (TLB Hit) - (大概率发生)
- TLB中正好有
0x12345
对应的条目。 - TLB直接返回缓存好的物理页框号 (Physical Page Frame, PFN),例如
0x87654
。 - MMU将这个PFN和原始的12位页内偏移
0x678
拼接起来,形成最终的物理地址0x87654678
。 - 地址转换完成! 整个过程极快,因为TLB是高速硬件。
- TLB中正好有
-
情况B:TLB 未命中 (TLB Miss) - (小概率发生)
- TLB中没有
0x12345
的映射记录。 - 此时,MMU必须启动“硬核”查询模式,即从内存中遍历多级页表。接下来的步骤都是在TLB Miss后发生的。
- TLB中没有
第 1 步:查询页目录 (一级查询)
MMU开始从内存中查找。
-
分解虚拟地址: MMU将虚拟地址
0x12345678
按照我们设定的结构进行分解。- 二进制表示:
0001 0010 0011 0100 0101 0110 0111 1000
- 页目录索引 (DIR): 最高10位 ->
0001001000
(十进制为72
) - 页表索引 (TABLE): 中间10位 ->
1101000101
(十进制为837
) - 页内偏移 (OFFSET): 最低12位 ->
011001111000
(十进制为1656
,十六进制为0x678
)
- 二进制表示:
-
定位页目录项 (PDE):
- MMU从 CR3寄存器 中读取到当前进程的页目录的物理基地址。假设这个地址是
0x10000000
。 - MMU计算PDE的物理地址:
PDE地址 = 页目录基地址 + (DIR × 每个PDE的大小)
PDE地址 = 0x10000000 + (72 × 4 Bytes) = 0x10000120
- MMU从 CR3寄存器 中读取到当前进程的页目录的物理基地址。假设这个地址是
-
读取并解析PDE:
- MMU向物理内存地址
0x10000120
发出请求,读取那4个字节的PDE内容。 - MMU检查这个PDE中的有效位 (Present Bit)。
- 如果有效位为0 (无效): 说明该PDE对应的整个二级页表都不存在(即这4MB的虚拟地址空间从未使用过)。MMU会立即停止,并触发一个缺页异常 (Page Fault)。操作系统内核接管,可能会去分配一个新的二级页表,然后再返回用户态继续执行。
- 如果有效位为1 (有效): 太好了!PDE是有效的。MMU从这个PDE中提取出二级页表的物理基地址。这个地址占据了PDE中的大部分位。假设提取出的地址是
0x11220000
。
- MMU向物理内存地址
第 2 步:查询二级页表 (二级查询)
现在我们有了二级页表的地址,可以进行下一步了。
-
定位页表项 (PTE):
- MMU使用上一步分解出的页表索引 (TABLE),即
837
。 - MMU计算PTE的物理地址:
PTE地址 = 二级页表基地址 + (TABLE × 每个PTE的大小)
PTE地址 = 0x11220000 + (837 × 4 Bytes) = 0x11220D14
- MMU使用上一步分解出的页表索引 (TABLE),即
-
读取并解析PTE:
- MMU向物理内存地址
0x11220D14
发出请求,读取那4个字节的PTE内容。 - MMU再次检查这个PTE中的有效位 (Present Bit)。
- 如果有效位为0 (无效): 说明这个虚拟页面本身不在物理内存中(可能从未被使用,或被换出到硬盘了)。MMU同样触发一个缺页异常 (Page Fault)。操作系统接管,可能会从硬盘把页面读回内存,或者分配一个新页面。
- 如果有效位为1 (有效): 成功在望!PTE是有效的。MMU从这个PTE中提取出最终的物理页框号 (PFN)。假设提取出的PFN是
0x87654
。
- MMU向物理内存地址
第 3 步:合成最终物理地址
-
拼接地址:
- MMU将上一步得到的物理页框号 (PFN)
0x87654
作为高位。 - MMU将第一步分解出的页内偏移 (OFFSET)
0x678
作为低位。 - 拼接成最终的物理地址:
0x87654
(PFN) +678
(OFFSET) =0x87654678
。
- MMU将上一步得到的物理页框号 (PFN)
-
访问数据:
- MMU将这个物理地址
0x87654678
交给总线,内存控制器会从这个地址读取数据,并返回给CPU的寄存器EAX。
- MMU将这个物理地址
第 4 步:更新TLB (后续优化)
在成功完成这次“硬核”查询后,MMU会把这次的映射关系缓存起来,以备后用。
- Key: 虚拟页号 (VPN)
0x12345
- Value: 物理页框号 (PFN)
0x87654
,以及相关的权限位等。
MMU将这个键值对写入TLB。这样,下一次CPU再访问 0x12345xxx
这个虚拟页内的任何地址时,就会直接TLB命中,跳过上面所有复杂的内存查询步骤。
扩展到64位四级页表
这个流程可以很自然地扩展。对于64位系统的四级页表,虚拟地址会被分解成更多部分:
| L4索引 | L3索引 | L2索引 | L1索引 | 页内偏移 |
查询流程就变成了:
- TLB Miss后…
- 查CR3,用L4索引找到L4表项,得到L3表的基地址。
- 查L3表,用L3索引找到L3表项,得到L2表的基地址。
- 查L2表,用L2索引找到L2表项,得到L1表的基地址。
- 查L1表,用L1索引找到L1表项,得到最终的物理页框号(PFN)。
- 拼接PFN和页内偏移,得到物理地址。
- 更新TLB。
虽然步骤变多了,但每一步的原理和逻辑是完全一样的:用索引定位表项,从表项中获取下一级表的基地址,直到最后一级获取到物理页框号。每一次内存访问都可能触发缺页异常。这就是多级页表查询的精髓所在。
Last updated on